﻿namespace Microsoft.Web.Helpers.Tests
{
    using System.IO;
    using System.Linq;
    using Microsoft.ServiceHosting.Tools.DevelopmentStorage;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.WindowsAzure;
    using Microsoft.WindowsAzure.StorageClient;

    [TestClass]
    public class WindowsAzureStorageHelperTests
    {
        private const string LocalDevStorageBlobUrl = @"http://127.0.0.1:10000/devstoreaccount1";
        private const string LocalDevStorageTableUrl = @"http://127.0.0.1:10002/devstoreaccount1";        
        private const string AccountName = "devstoreaccount1";
        private const string AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";

        [ClassInitialize]
        public static void Initialize(TestContext context)
        {
            // Ensuring the Dev Storage is running
            var devStore = new DevStore();
            if (!devStore.IsRunning())
            {
                devStore.EnsureRunning(100000);
            }

            // Creating test data for the Table Service
            var tableServiceContext = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            tableServiceContext.AddObject("Tables", new TestTable() { TableName = "TestTableForQuery" });
            tableServiceContext.AddObject("Tables", new TestTable() { TableName = "TestTableForInsert" });
            tableServiceContext.AddObject("Tables", new TestTable() { TableName = "TestTableForUpdate" });
            tableServiceContext.AddObject("Tables", new TestTable() { TableName = "TestTableForDelete" });
            tableServiceContext.AddObject("TestTableForQuery", new TestEntity() { RowKey = "row1", PartitionKey = "partition1", TestContent = "TestContent" });
            tableServiceContext.AddObject("TestTableForQuery", new TestEntity() { RowKey = "row2", PartitionKey = "partition1", TestContent = "TestContent" });
            tableServiceContext.AddObject("TestTableForUpdate", new TestEntity() { RowKey = "row1", PartitionKey = "partition1", TestContent = "TestContent" });
            tableServiceContext.AddObject("TestTableForDelete", new TestEntity() { RowKey = "row1", PartitionKey = "partition1", TestContent = "TestContent" });
            tableServiceContext.SaveChanges();

            // Creating test data for the Blob Service
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blobContainer = blobClient.GetContainerReference("images");
            blobContainer.CreateIfNotExist();            
            blobContainer = blobClient.GetContainerReference("text");
            blobContainer.CreateIfNotExist();
            blobContainer = blobClient.GetContainerReference("textitems");
            blobContainer.CreateIfNotExist();
            var blob = blobContainer.GetBlobReference("item1");
            blob.UploadText("Text Content 1");
            blob = blobContainer.GetBlobReference("item2");
            blob.UploadText("Text Content 2");
            blob = blobContainer.GetBlobReference("item3/with/slashes");
            blob.UploadText("Text Content 3");
        }

        [ClassCleanup]
        public static void Cleanup()
        {
            // Returning ATS to original state
            var serviceContext = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var table1 = serviceContext.CreateQuery<TestTable>("Tables").Where(t => t.TableName == "TestTableForQuery").FirstOrDefault();
            var table2 = serviceContext.CreateQuery<TestTable>("Tables").Where(t => t.TableName == "TestTableForInsert").FirstOrDefault();
            var table3 = serviceContext.CreateQuery<TestTable>("Tables").Where(t => t.TableName == "TestTableForUpdate").FirstOrDefault();
            var table4 = serviceContext.CreateQuery<TestTable>("Tables").Where(t => t.TableName == "TestTableForDelete").FirstOrDefault();            
            
            serviceContext.DeleteObject(table1);
            serviceContext.DeleteObject(table2);
            serviceContext.DeleteObject(table3);
            serviceContext.DeleteObject(table4);
            serviceContext.SaveChanges();
            
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blobContainer = blobClient.GetContainerReference("images");
            blobContainer.Delete();
            blobContainer = blobClient.GetContainerReference("text");
            blobContainer.Delete();
            blobContainer = blobClient.GetContainerReference("textitems");
            blobContainer.Delete();

            // Shutting down the Dev Storage
            var devStore = new DevStore();
            if (devStore.IsRunning())
            {
                devStore.Shutdown();
            }
        }

        [TestMethod]
        public void ShouldRetrieveRowsFromStorageUsingODataHelper()
        {
            var results = OData.Get(
                        "http://127.0.0.1:10002/devstoreaccount1" + "/TestTableForQuery", 
                        new SharedKeyLiteAuthenticationScheme(AccountName, AccountKey));

            Assert.AreEqual(2, results.Count());
            Assert.IsNotNull(results[0]);
            Assert.IsNotNull(results[0].PartitionKey);
            Assert.IsNotNull(results[0].RowKey);
            Assert.IsNotNull(results[0].TestContent);
        }

        [TestMethod]
        public void ShouldRetrieveRowsFromTableUsingStorageAzureHelper()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetRows("TestTableForQuery");

            Assert.AreEqual(2, results.Count());
            Assert.IsNotNull(results[0]);
            Assert.IsNotNull(results[0].PartitionKey);
            Assert.IsNotNull(results[0].RowKey);
            Assert.IsNotNull(results[0].TestContent);
        }

        [TestMethod]
        public void ShouldRetrieveSingleRowFromTable()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var row = WindowsAzureStorage.GetRow("TestTableForQuery", "partition1", "row1");

            Assert.IsNotNull(row);
            Assert.IsNotNull(row.PartitionKey);
            Assert.IsNotNull(row.RowKey);
            Assert.IsNotNull(row.TestContent);
        }

        [TestMethod]
        public void ShouldCheckExistenceOfTables()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            Assert.IsTrue(WindowsAzureStorage.TableExists("TestTableForQuery"));
            Assert.IsFalse(WindowsAzureStorage.TableExists("InexistentTable"));
        }

        [TestMethod]
        public void ShouldRetrieveTables()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetTables();

            Assert.IsTrue(results.Count() > 0);
            Assert.IsNotNull(results[0]);
            Assert.IsNotNull(results[0].TableName);
        }

        [TestMethod]
        public void ShouldCreateNewTableInStorage()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.CreateTable("NewTestTable");

            var context = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var table = context.CreateQuery<TestTable>("Tables").Where(t => t.TableName == "NewTestTable").FirstOrDefault();

            Assert.IsNotNull(table);

            context.DeleteObject(table);
            context.SaveChanges();
        }

        [TestMethod]
        public void ShouldDeleteTableFromStorage()
        {
            var context = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            context.AddObject("Tables", new TestTable() { TableName = "TestTableToDelete" });
            context.SaveChanges();

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.DeleteTable("TestTableToDelete");
            
            var tables = context.CreateQuery<TestTable>("Tables").Execute();

            Assert.IsNull(tables.Where(t => t.TableName == "TestTableToDelete").FirstOrDefault());            
        }

        [TestMethod]
        public void ShouldInsertNewRowInTable()
        {
            var rowToInsert = WindowsAzureStorage.CreateRow("partition1", "row1");
            rowToInsert.TestContent = "SomeContent";

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.InsertRow("TestTableForInsert", rowToInsert);

            var context = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var insertedrow = context.CreateQuery<TestEntity>("TestTableForInsert").Where(e => e.RowKey == "row1" && e.PartitionKey == "partition1").FirstOrDefault();

            Assert.IsNotNull(insertedrow);
            Assert.AreEqual(rowToInsert.TestContent, insertedrow.TestContent);
        }

        [TestMethod]
        public void ShouldUpdateRowInTable()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetRows("TestTableForUpdate(PartitionKey='partition1',RowKey='row1')");
            
            var rowToUpdate = results[0];
            rowToUpdate.TestContent = "TestContentUpdated";

            WindowsAzureStorage.UpdateRow("TestTableForUpdate", rowToUpdate);

            var context = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var updatedRow = context.CreateQuery<TestEntity>("TestTableForUpdate").Where(e => e.RowKey == "row1" && e.PartitionKey == "partition1").FirstOrDefault();

            Assert.IsNotNull(updatedRow);
            Assert.AreEqual(rowToUpdate.TestContent, updatedRow.TestContent);            
        }

        [TestMethod]
        public void ShouldDeleteRowFromStorage()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.DeleteRow("TestTableForDelete", "partition1", "row1");

            var context = new TableServiceContext(LocalDevStorageTableUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));            

            var rowsInTable = context.CreateQuery<TestEntity>("TestTableForDelete").Execute();

            Assert.IsNull(rowsInTable.Where(t => t.RowKey == "row1" & t.PartitionKey == "partition1").FirstOrDefault());                        
        }

        [TestMethod]
        public void ShouldUseWhereOperator()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetRows("TestTableForQuery", "RowKey eq 'row1'");

            Assert.AreEqual(1, results.Count());
            Assert.IsNotNull(results[0]);
            Assert.AreEqual(results[0].RowKey, "row1");                        
            Assert.IsNotNull(results[0].TestContent);
        }

        [TestMethod]
        public void ShouldUseTakeOperator()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetRows("TestTableForQuery", take: 1);

            Assert.AreEqual(1, results.Count());
            Assert.IsNotNull(results[0]);
            Assert.IsNotNull(results[0].PartitionKey);
            Assert.IsNotNull(results[0].RowKey);
            Assert.IsNotNull(results[0].TestContent);
        }

        [TestMethod]
        public void ShouldUseWhereAndTakeOperators()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var results = WindowsAzureStorage.GetRows("TestTableForQuery", "PartitionKey eq 'partition1'", 1);

            Assert.AreEqual(1, results.Count());
            Assert.IsNotNull(results[0]);
            Assert.AreEqual(results[0].PartitionKey, "partition1");                          
            Assert.IsNotNull(results[0].RowKey);
            Assert.IsNotNull(results[0].TestContent);
        }

        [TestMethod]
        [DeploymentItem("Resources/Koala.jpg")]
        public void ShouldUploadToBlobFromStream()
        {
            using (var streamToUpload = new FileStream("Koala.jpg", FileMode.Open))
            {
                WindowsAzureStorage.AccountName = AccountName;
                WindowsAzureStorage.AccountKey = AccountKey;

                WindowsAzureStorage.UploadBinaryToBlob("images/koala", streamToUpload);
            }

            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/koala");

            var downloadedBytes = blob.DownloadByteArray();
            var uploadedBytes = File.ReadAllBytes("Koala.jpg");

            Assert.AreEqual(uploadedBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < uploadedBytes.Length; i++)
            { 
                areEqual = uploadedBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        [DeploymentItem("Resources/Penguins.jpg")]
        public void ShouldUploadToBlobFromFile()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.UploadBinaryToBlob("images/penguins", "Penguins.jpg");

            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/penguins");

            var downloadedBytes = blob.DownloadByteArray();
            var uploadedBytes = File.ReadAllBytes("Penguins.jpg");

            Assert.AreEqual(uploadedBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < uploadedBytes.Length; i++)
            {
                areEqual = uploadedBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        [DeploymentItem("Resources/Tulips.jpg")]
        public void ShouldUploadToBlobFromByteArray()
        {
            var uploadedBytes = File.ReadAllBytes("Tulips.jpg");

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.UploadBinaryToBlob("images/tulips", uploadedBytes);

            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/tulips");

            var downloadedBytes = blob.DownloadByteArray();            

            Assert.AreEqual(uploadedBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < uploadedBytes.Length; i++)
            {
                areEqual = uploadedBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        public void ShouldUploadTextToBlob()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.UploadTextToBlob("text/uploadedtext", "Uploading Hello World!");

            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("text/uploadedtext");
            var downloadedText = blob.DownloadText();

            Assert.AreEqual("Uploading Hello World!", downloadedText);
        }

        [TestMethod]
        [DeploymentItem("Resources/Desert.jpg")]
        public void ShouldDownloadBlobAsStream()
        {
            var originalBytes = File.ReadAllBytes("Desert.jpg");
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/desert");
            blob.UploadByteArray(originalBytes);

            byte[] downloadedBytes;
            using (var stream = new MemoryStream())
            {
                WindowsAzureStorage.AccountName = AccountName;
                WindowsAzureStorage.AccountKey = AccountKey;

                WindowsAzureStorage.DownloadBlobAsStream("images/desert", stream);
                stream.Position = 0;
                downloadedBytes = new byte[stream.Length];
                stream.Read(downloadedBytes, 0, (int)stream.Length);
            }

            Assert.AreEqual(originalBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < originalBytes.Length; i++)
            {
                areEqual = originalBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        [DeploymentItem("Resources/Desert.jpg")]
        public void ShouldDownloadBlobToFile()
        {
            var originalBytes = File.ReadAllBytes("Desert.jpg");
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/desert");
            blob.UploadByteArray(originalBytes);

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.DownloadBlobToFile("images/desert", "Desert.downloaded.jpg");

            Assert.IsTrue(File.Exists("Desert.downloaded.jpg"));

            var downloadedBytes = File.ReadAllBytes("Desert.downloaded.jpg");

            Assert.AreEqual(originalBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < originalBytes.Length; i++)
            {
                areEqual = originalBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        [DeploymentItem("Resources/Desert.jpg")]
        public void ShouldDownloadBlobAsByteArray()
        {
            var originalBytes = File.ReadAllBytes("Desert.jpg");
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("images/desert");
            blob.UploadByteArray(originalBytes);

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var downloadedBytes = WindowsAzureStorage.DownloadBlobAsByteArray("images/desert");

            Assert.AreEqual(originalBytes.Length, downloadedBytes.Length);
            var areEqual = true;
            for (int i = 0; i < originalBytes.Length; i++)
            {
                areEqual = originalBytes[i] == downloadedBytes[i];
            }

            Assert.IsTrue(areEqual);
        }

        [TestMethod]
        public void ShouldDownloadBlobAsText()
        {
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("text/texttodownload");
            blob.UploadText("Downlading Hello World!");

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var downloadedText = WindowsAzureStorage.DownloadBlobAsText("text/texttodownload");

            Assert.AreEqual("Downlading Hello World!", downloadedText);
        }

        [TestMethod]
        public void ShouldCreateBlobContainer()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.CreateBlobContainer("newcontainer");

            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var container = blobClient.ListContainers().FirstOrDefault(c => c.Name == "newcontainer");
                
            Assert.IsNotNull(container);
            container.Delete();
        }

        [TestMethod]
        public void ShouldListBlobItemsFromContainer()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var blobs = WindowsAzureStorage.ListBlobs("textitems");

            Assert.AreEqual(3, blobs.Count());
            Assert.AreEqual("textitems/item1", blobs[0]);
            Assert.AreEqual("textitems/item2", blobs[1]);
            Assert.AreEqual("textitems/item3/with/slashes", blobs[2]);
        }

        [TestMethod]
        public void ShouldListBlobContainers()
        {
            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            var containers = WindowsAzureStorage.ListBlobContainers();

            Assert.IsTrue(containers.Count() >= 3);
            Assert.IsTrue(containers.Contains("images"));
            Assert.IsTrue(containers.Contains("text"));
            Assert.IsTrue(containers.Contains("textitems"));
        }

        [TestMethod]
        public void ShouldDeleteBlobContainer()
        {
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var container = blobClient.GetContainerReference("containertodelete");
            container.CreateIfNotExist();

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.DeleteBlobContainer("containertodelete");

            container = blobClient.ListContainers().FirstOrDefault(c => c.Name == "newcontainer");
            Assert.IsNull(container);
        }

        [TestMethod]
        public void ShouldDeleteBlob()
        {
            var blobClient = new CloudBlobClient(LocalDevStorageBlobUrl, new StorageCredentialsAccountAndKey(AccountName, AccountKey));
            var blob = blobClient.GetBlobReference("text/textblobtodelete");
            blob.UploadText("Text Blob to delete");

            WindowsAzureStorage.AccountName = AccountName;
            WindowsAzureStorage.AccountKey = AccountKey;

            WindowsAzureStorage.DeleteBlob("text/textblobtodelete");

            var blobItem = blobClient.ListBlobsWithPrefix("text/textblobtodelete").FirstOrDefault();
            Assert.IsNull(blobItem);
        }
    }
}
